/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package main;

/**
 *
 * @author ZSQ
 */
/**
 * 遗传算法训练器
 * 由遗传算法专家实现
 */
import java.util.Random;

public class GeneticAlgorithmTrainer implements Trainer {
    private int populationSize;
    private double mutationRate;
    private int generations;
    private double[] lossHistory;
    
    public GeneticAlgorithmTrainer(int populationSize, double mutationRate, int generations) {
        this.populationSize = populationSize;
        this.mutationRate = mutationRate;
        this.generations = generations;
        this.lossHistory = new double[generations];
    }
    
    @Override
    public void train(NeuralNetwork network, double[][] trainingData, double[][] targets) {
        System.out.println("开始遗传算法训练...");
        System.out.printf("种群大小: %d, 变异率: %.3f, 最大代数: %d\n", 
                         populationSize, mutationRate, generations);
        
        // 创建初始种群
        double[][] population = new double[populationSize][];
        double[] fitness = new double[populationSize];
        
        // 初始化种群 - 使用更小的初始权重范围
        for (int i = 0; i < populationSize; i++) {
            population[i] = encodeNetworkToChromosome(network);
            // 重新初始化权重为小值
            for (int j = 0; j < population[i].length; j++) {
                population[i][j] = (Math.random() - 0.5) * 0.1;
            }
            fitness[i] = calculateFitness(network, population[i], trainingData, targets);
        }
        
        for (int gen = 0; gen < generations; gen++) {
            // 创建新种群
            double[][] newPopulation = new double[populationSize][];
            
            // 精英保留：保留前10%的最佳个体
            int eliteCount = Math.max(1, populationSize / 10);
            for (int i = 0; i < eliteCount; i++) {
                int bestIndex = findBestIndividual(fitness);
                newPopulation[i] = population[bestIndex].clone();
                fitness[bestIndex] = Double.MAX_VALUE; // 避免重复选择
            }
            
            // 生成剩余个体
            for (int i = eliteCount; i < populationSize; i++) {
                // 选择父母
                int parent1 = tournamentSelection(fitness, 5);
                int parent2 = tournamentSelection(fitness, 5);
                
                // 交叉
                newPopulation[i] = crossover(population[parent1], population[parent2]);
                
                // 变异
                mutate(newPopulation[i]);
            }
            
            population = newPopulation;
            
            // 计算新种群的适应度
            double bestFitness = Double.MAX_VALUE;
            int currentBestIndex = 0;
            for (int i = 0; i < populationSize; i++) {
                fitness[i] = calculateFitness(network, population[i], trainingData, targets);
                if (fitness[i] < bestFitness) {
                    bestFitness = fitness[i];
                    currentBestIndex = i;
                }
            }
            
            // 将最佳个体解码到网络
            decodeChromosomeToNetwork(network, population[currentBestIndex]);
            lossHistory[gen] = bestFitness;
            
            if (gen % 50 == 0 || gen < 10) {
                System.out.printf("Generation %d, Best Loss: %.6f\n", gen, bestFitness);
            }
            
            // 改进的早停条件
            if (bestFitness < 0.01) {
                System.out.printf("遗传算法在第%d代达到目标精度\n", gen);
                break;
            }
            
            if (gen > 100 && Math.abs(bestFitness - lossHistory[gen-50]) < 1e-6) {
                System.out.printf("遗传算法在第%d代收敛\n", gen);
                break;
            }
        }
    }
    
    private double[] encodeNetworkToChromosome(NeuralNetwork network) {
        Layer[] layers = network.getLayers();
        
        // 计算染色体总长度
        int totalLength = 0;
        for (Layer layer : layers) {
            Neuron[] neurons = layer.getNeurons();
            for (Neuron neuron : neurons) {
                totalLength += neuron.getWeights().length + 1; // 权重 + 偏置
            }
        }
        
        double[] chromosome = new double[totalLength];
        int index = 0;
        
        for (Layer layer : layers) {
            Neuron[] neurons = layer.getNeurons();
            for (Neuron neuron : neurons) {
                // 编码权重
                double[] weights = neuron.getWeights();
                System.arraycopy(weights, 0, chromosome, index, weights.length);
                index += weights.length;
                
                // 编码偏置
                chromosome[index++] = neuron.getBias();
            }
        }
        
        return chromosome;
    }
    
    private void decodeChromosomeToNetwork(NeuralNetwork network, double[] chromosome) {
        Layer[] layers = network.getLayers();
        int index = 0;
        
        for (Layer layer : layers) {
            Neuron[] neurons = layer.getNeurons();
            for (Neuron neuron : neurons) {
                // 解码权重
                double[] weights = neuron.getWeights();
                System.arraycopy(chromosome, index, weights, 0, weights.length);
                index += weights.length;
                
                // 解码偏置
                neuron.setBias(chromosome[index++]);
            }
        }
    }
    
    private double calculateFitness(NeuralNetwork network, double[] chromosome, 
                                   double[][] trainingData, double[][] targets) {
        decodeChromosomeToNetwork(network, chromosome);
        return network.calculateLoss(trainingData, targets);
    }
    
    private int findBestIndividual(double[] fitness) {
        int bestIndex = 0;
        for (int i = 1; i < fitness.length; i++) {
            if (fitness[i] < fitness[bestIndex]) {
                bestIndex = i;
            }
        }
        return bestIndex;
    }
    
    private int tournamentSelection(double[] fitness, int tournamentSize) {
        int bestIndex = (int) (Math.random() * fitness.length);
        for (int i = 1; i < tournamentSize; i++) {
            int candidate = (int) (Math.random() * fitness.length);
            if (fitness[candidate] < fitness[bestIndex]) {
                bestIndex = candidate;
            }
        }
        return bestIndex;
    }
    
    private double[] crossover(double[] parent1, double[] parent2) {
        double[] child = new double[parent1.length];
        
        // 均匀交叉
        for (int i = 0; i < parent1.length; i++) {
            if (Math.random() < 0.5) {
                child[i] = parent1[i];
            } else {
                child[i] = parent2[i];
            }
        }
        
        return child;
    }
    
    private void mutate(double[] chromosome) {
        for (int i = 0; i < chromosome.length; i++) {
            if (Math.random() < mutationRate) {
                // 高斯变异
                chromosome[i] += (Math.random() - 0.5) * 0.5;
                // 限制范围
                chromosome[i] = Math.max(-2, Math.min(2, chromosome[i]));
            }
        }
    }
    
    @Override
    public double[] getLossHistory() { return lossHistory; }
    
    @Override
    public String getName() { return "遗传算法"; }
}